mod rpc_cli;
mod rpc_server;

use crate::rpc_cli::Command::{Add, Get, Remove, UpdatePrice, UpdateQuantity, Watch};
use crate::rpc_cli::{add, get, remove, update_price, update_quantity, watch, Options};
use crate::rpc_server::StoreInventory;
use grpc_gen::inventory_server::InventoryServer;
use ntex::web;
use std::sync::LazyLock;
use tonic_reflection::proto::FILE_DESCRIPTOR_SET;

static FOO: LazyLock<String> = LazyLock::new(|| "xxx".to_string());

#[web::get("/")]
async fn hello() -> impl web::Responder {
    web::HttpResponse::Ok().body("Hello world!")
}

#[web::post("/echo")]
async fn echo(req_body: String) -> impl web::Responder {
    web::HttpResponse::Ok().body(req_body)
}

async fn handle_opts(opts: Options) -> Result<String, Box<dyn std::error::Error>> {
    match opts.command {
        Add(opts) => add(opts).await,
        Remove(opts) => remove(opts).await,
        Get(opts) => get(opts).await,
        UpdateQuantity(opts) => update_quantity(opts).await,
        UpdatePrice(opts) => update_price(opts).await,
        Watch(opts) => watch(opts).await,
    }
}

#[web::post("/rpc")]
async fn echo_rpc(req_body: String) -> impl web::Responder {
    let opts: Options = sonic_rs::from_str(&req_body).unwrap();
    match handle_opts(opts).await {
        Ok(res) => web::HttpResponse::Ok().body(res),
        Err(err) => web::HttpResponse::Ok().body(err.to_string()),
    }
}

async fn manual_hello() -> impl web::Responder {
    web::HttpResponse::Ok().body(&*FOO)
}

#[ntex::main]
async fn main() -> std::io::Result<()> {
    tokio::spawn(async move {
        let addr = "127.0.0.1:9001".parse().unwrap();
        let inventory = StoreInventory::default();

        let reflection_service = tonic_reflection::server::Builder::configure()
            .register_encoded_file_descriptor_set(FILE_DESCRIPTOR_SET)
            .build()
            .unwrap();

        tonic::transport::Server::builder()
            .add_service(InventoryServer::new(inventory))
            .add_service(reflection_service)
            .serve(addr)
            .await
            .unwrap();
    });
    let server = web::HttpServer::new(|| {
        web::App::new()
            .service(hello)
            .service(echo)
            .service(echo_rpc)
            .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run();

    server.await
}
